#include <cjsonparser.h>
#include <cjson.h>
#include <list>
#include <stdio.h>

using namespace std;

#ifdef CJSON_USE_UNICODE
//#define //fprintf fwprintf
#else
//#define //fprintf stub
#endif

//void stub(FILE * f, const jchar * f, ..)
//{
//}

CJsonParser::CJsonParser()
{
//  f = fopen("json.log", "a+");
//  //fprintf(f, J("Parser created\n"));
}

struct fifo_node
{
  CJsonValue *obj;
  list <CJsonPare*> pares_fifo;
  fifo_node()
  {
    obj = NULL;
  }
  fifo_node(CJsonValue *obj, CJsonPare *pare)
  {
    this->obj = obj;
    pares_fifo.push_back(pare);
  }
};

long small_pow(long base, unsigned grade)
{
  long result = 1;
  for (unsigned i = 0; i < grade; i++)
  {
    result *= base;
  }
  return result;
}

long number_from_list(list <unsigned short> & temp_number_value)
{
  long temp_number = 0;
  for (size_t i = 0; !temp_number_value.empty(); i++)
  {
    temp_number += temp_number_value.back() * small_pow(10, i);
    temp_number_value.pop_back();
  }
  return temp_number;
}

CJsonValue * recognize_unknown(jstring & temp_string)
{
  if (temp_string == JSON_TRUE)
  {
    return new CJsonValueBool(true);
  }
  else if (temp_string == JSON_FALSE)
  {
    return new CJsonValueBool(false);
  }
  else if (temp_string == JSON_NULL)
  {
    return new CJsonValueNull();
  }
  else
  {
    throw CJsonError(JE_PARSE_FAILED);
  }
  return NULL;
}

CJsonObject * CJsonParser::ParseJson(jstring & json_string)
{
  //fprintf(f, J(">> CJsonParse::ParseJson\n"));
  enum CJsonParseFlags
  {
    PF_BEGIN         =0 ,
    PF_NAME_START   ,
    PF_NAME_END     ,
    PF_STRING_START ,
    PF_STRING_END   ,
    PF_VALUE        ,
    PF_COLON        ,
    PF_ARRAY_END    ,
    PF_OBJECT_END   ,
    PF_NUMBER_END   , 
    PF_UNKNOWN_END  ,
    PF_NEXT_OR_END
  } flags = PF_BEGIN;

  list < fifo_node * > fifo;
  CJsonObject *json_object = NULL;
  list <unsigned short> temp_number_value;
  jstring temp_string;
  jchar string_quote;
  //fprintf(f, J("Starting loop\n"));
  for (unsigned i = 0; i < json_string.size(); i++)
  {
    //fprintf(f, J("------------------------------\n"));
    //fprintf(f, J("iter %i (Flags: %d)\n"), i, (int)flags);
    //fprintf(f, J("temp_string: %s\n"), temp_string.c_str());
    //fprintf(f, J("fifo size: %d\n"), fifo.size());
    switch (json_string[i])
    {
      case J('{'): //starting new object
        //fprintf(f, J("{\n"));
        switch (flags)
        {
          case PF_BEGIN:
          case PF_VALUE:
            //fprintf(f, J("O--> New Obj started\n"));
            //fprintf(f, J("P--> New Pare Added\n"));
            fifo.push_back( new fifo_node(new CJsonObject(), new CJsonPare()) );
            flags = PF_NAME_START;
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J('['): //starting new array
        //fprintf(f, J("[\n"));
        switch (flags)
        {
          case PF_VALUE:
            //fprintf(f, J("A--> New Array started\n"));
            fifo.push_back( new fifo_node((CJsonValue*)(new CJsonArray()), new CJsonPare()) );
            flags = PF_VALUE;
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J('\''): //some string
      case J('"'): 
        //fprintf(f, J("\"\n"));
        switch (flags)
        {
          case PF_NAME_START:
            temp_string.clear();
            string_quote = json_string[i];
            flags = PF_NAME_END;
          break;
          case PF_STRING_END:
            if (fifo.back()->pares_fifo.empty())
            {
              throw CJsonError(JE_PARSE_FAILED);
            }
            else
            {
              if (string_quote == json_string[i])
              {
                fifo.back()->pares_fifo.back()->AddValue(new CJsonValueString(temp_string));
                //fprintf(f, J("V--> New Value (%s)\n"), temp_string.c_str());
                flags = PF_NEXT_OR_END;
              }
              else
              {
                temp_string += json_string[i];
              }
            }
          break;
          case PF_NAME_END:
            if (fifo.back()->pares_fifo.empty())
            {
              throw CJsonError(JE_PARSE_FAILED);
            }
            else
            {
              if (string_quote == json_string[i])
              {
                fifo.back()->pares_fifo.back()->AddName(new CJsonName(temp_string));
                //fprintf(f, J("N--> New Name (%s)\n"), temp_string.c_str());
                flags = PF_COLON;
              }
              else
              {
                temp_string += json_string[i];
              }
            }
          break;
          case PF_VALUE:
            temp_string.clear();
            string_quote = json_string[i];
            flags = PF_STRING_END;
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J(':'): //colon
        //fprintf(f, J(":\n"));
        switch (flags)
        {
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_COLON:
            flags = PF_VALUE;
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J(','): //comma
        //fprintf(f, J(",\n"));
        switch (flags)
        {
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_NUMBER_END:
            fifo.back()->pares_fifo.back()->AddValue(new CJsonValueNumber(number_from_list(temp_number_value)));
          case PF_UNKNOWN_END:
            if (flags == PF_UNKNOWN_END)
            {
              fifo.back()->pares_fifo.back()->AddValue(recognize_unknown(temp_string));
            }
          case PF_NEXT_OR_END:
            //fprintf(f, J("P--> New Pare Added\n"));
            fifo.back()->pares_fifo.push_back(new CJsonPare());
            //fprintf(f, J("obj type: %d\n"), fifo.back()->obj->GetType());
            if (fifo.back()->obj->GetType() == JV_ARRAY)
            {
              flags = PF_VALUE;
            }
            else
            {
              flags = PF_NAME_START;
            }
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J('}'): //object end
        //fprintf(f, J("}\n"));
        //fprintf(f, J("(%d)\n"), (int)flags);
        switch (flags)
        {
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_NUMBER_END:
            fifo.back()->pares_fifo.back()->AddValue(new CJsonValueNumber(number_from_list(temp_number_value)));
          case PF_UNKNOWN_END:
            if (flags == PF_UNKNOWN_END)
            {
              fifo.back()->pares_fifo.back()->AddValue(recognize_unknown(temp_string));
            }
          case PF_NEXT_OR_END:
          {
            fifo_node *node = fifo.back();
            fifo.pop_back();
            while (!node->pares_fifo.empty())
            {
              ((CJsonObject*)node->obj)->AddPare(node->pares_fifo.front());
              node->pares_fifo.pop_front();
            }
            if (fifo.empty())
            {
              json_object = (CJsonObject*)node->obj;
              delete node;
              //fprintf(f, J("END FOUND\n"));
              break;
            }
            else
            {
              fifo.back()->pares_fifo.back()->AddValue((CJsonValue*)node->obj);
              //fprintf(f, J("Obj added (%s)\n"), node->obj->ToString().c_str());
            }
            delete node;
            //fprintf(f, J("O--> Obj END\n"));
            flags = PF_NEXT_OR_END;
          }
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J(']'): //array end
        //fprintf(f, J("]\n"));
        //fprintf(f, J("(%d)\n"), (int)flags);
        switch (flags)
        {
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_NUMBER_END:
            fifo.back()->pares_fifo.back()->AddValue(new CJsonValueNumber(number_from_list(temp_number_value)));
          case PF_UNKNOWN_END:
            if (flags == PF_UNKNOWN_END)
            {
              fifo.back()->pares_fifo.back()->AddValue(recognize_unknown(temp_string));
            }
          case PF_NEXT_OR_END:
          {
            fifo_node *node = fifo.back();
            fifo.pop_back();
            while (!node->pares_fifo.empty())
            {
              if (node->obj->GetType() == JV_ARRAY)
              {
                ((CJsonArray*)node->obj)->AddMember(node->pares_fifo.front()->GetValue());
              }
              else
              {
                ((CJsonObject*)node->obj)->AddPare(node->pares_fifo.front());
              }
              node->pares_fifo.pop_front();
            }
            if (fifo.empty())
            {
              /*json_object = node->obj;
              delete node;
              //fprintf(f, J("END FOUND\n"));
              break;*/
            }
            else
            {
              fifo.back()->pares_fifo.back()->AddValue((CJsonValue*)node->obj);
              //fprintf(f, J("Array added (%s)\n"), node->obj->ToString().c_str());
            }
            delete node;
            //fprintf(f, J("O--> Array END\n"));
            flags = PF_NEXT_OR_END;
          }
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J(' '): //space
        //fprintf(f, J("' '\n"));
        switch (flags)
        {
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_NUMBER_END:
            fifo.back()->pares_fifo.back()->AddValue(new CJsonValueNumber(number_from_list(temp_number_value)));
            flags = PF_NEXT_OR_END;
          break;
          case PF_UNKNOWN_END:
            fifo.back()->pares_fifo.back()->AddValue(recognize_unknown(temp_string));
            flags = PF_NEXT_OR_END;
          break;
          default:{}
            //no error
        }
      break;
      case J('0'):
      case J('1'):
      case J('2'):
      case J('3'):
      case J('4'):
      case J('5'):
      case J('6'):
      case J('7'):
      case J('8'):
      case J('9'):
        //fprintf(f, J("numbers (%c)\n"), json_string[i]);
        switch (flags)
        {
          case PF_VALUE:
            temp_number_value.clear();
            temp_number_value.push_back(json_string[i] - J('0'));
            flags = PF_NUMBER_END;
          break;
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_NUMBER_END:
            temp_number_value.push_back(json_string[i] - J('0'));
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
      break;
      case J('\0'):
      {
        //fprintf(f, J("END\n"));
        break;
      }
      break;
      default:
        //fprintf(f, J("default (%c)\n"), json_string[i]);
        switch (flags)
        {
          case PF_VALUE:
            temp_string.clear();
            temp_string += json_string[i];
            flags = PF_UNKNOWN_END;
          break;
          case PF_UNKNOWN_END:
          case PF_STRING_END:
          case PF_NAME_END:
            temp_string += json_string[i];
          break;
          case PF_NUMBER_END:
            temp_number_value.push_back(json_string[i] - J('0'));
          break;
          default:
            throw CJsonError(JE_PARSE_FAILED);
        }
    }
  }
  //fprintf(f, J("<< CJsonParser::ParseJson\n"));
  return json_object;
}

